/**
 * @author A. Lothore
 * @classDescription GUI core object : base class of all data objects
 */

 
 GUI.prototype = {};
 
 function GUI(attributes)
 {
 	this.client_id	= getNewGUIId();
	this.visible	= false;
	
	//Attributes
	this.unique_id                  = null;
	this.object_id                   = null;
	this.parent                     = null;
	this.object_type                = "GenericObject";
	this.deleted                    = null;
	this.order_rank                 = null;
	this.deleted_at                 = null;
	this.created_at                 = null;
	this.attributes                 = new GUIAttributes();
	this.setAttribute               = GUI_setAttribute;
	this.setAttributes              = GUI_setAttributes;
	this.getAttribute               = GUI_getAttribute;
	this.getAttributes              = GUI_getAttributes;
	this.setAttributeByName         = GUI_setAttributeByName;
	this.setCommonAttributeByName   = GUI_setCommonAttributeByName;
	
	//Operations
	this.validate               = GUI_validate;
	this.applyEdit              = GUI_applyEdit;
	this.serialize              = GUI_serialize;
	this.toJSON                 = GUI_toJSON;
	this.toJSONP                = GUI_toJSON_RPC;
	this.commonAttributeToJSON  = GUI_commonAttributeToJSON;
	this.setDragAndDrop         = GUI_setDragAndDrop;
	this.setRank                = GUI_setRank;
	
	//Views
	this.views = [];
	this.addViewName  = GUI_addViewName;
	this.isViewName   = GUI_isViewName;
	this.getView      = GUI_getView;
	this.getViewId    = GUI_getViewId;
	this.goToView     = GUI_goToView;
	this.deleteViews  = GUI_deleteViews;
	
	//Views Cache
	this.viewsCache       = {};
	this.getViewFromCache = GUI_getViewFromCache;
	
	//Parent List
	this.parentList     = null;
	this.setParentList  = GUI_setParentList;
	
	//Current views //TODO : is it really useful ??
	this.currentViews           = []; //a list of view names
	this.addCurrentViewName     = GUI_addCurrentViewName;
	this.removeCurrentViewName  = GUI_removeCurrentViewName;
	this.isVisible              = GUI_isVisible;
	
	//Patterns
	this.getPatternId       	= GUI_getPatternId;
	this.determinePatternId 	= GUI_determinePatternId;
	this.mapPattern         	= GUI_mapPattern;
	this.groupPatternsObject	= null; // TODO : make it an array[viewName] to handle multiple grouping ?
	
	//Show-hide
	this.show     = GUI_show;
	this.showAll  = GUI_showAll;
	this.hide     = GUI_hide;
	this.hideAll  = GUI_hideAll;
	this.refresh  = GUI_refresh;
	
	//Data Access
	this.serverInterface  = new ServerInterface();
	this.setObjectType    = GUI_setObjectType;
	this.setDataSource    = GUI_setDataSource;
	this.setCallMethod    = GUI_setCallMethod;
	this.getCallMethod    = GUI_getCallMethod;
	this.onRemoteCall     = function() {};
	this.save             = GUI_save;
	this.onSaveCommon     = GUI_onSaveCommon;
	this.onSave           = GUI_onSave;
	this.remove           = GUI_remove;
 	this.onRemoveCommon   = GUI_onRemoveCommon;
	this.onRemove         = GUI_onRemove;	
	
	//Private
	this.applyOnViewNames         = GUI_applyOnViewNames;
	this.applyOnCurrentViewsNames = GUI_applyOnCurrentViewsNames;
	
	//Intialize
	addToGUIs(this);
	this.setAttributes(attributes);
 }
 
 
//--------------------------------------------------------------------------------------------------------------------------
/*
 * Adds a view name to the view list 
 * @viewName : the view name to add
 */
function GUI_addViewName(viewName)
{
	var index = this.isViewName(viewName);
	if (!index) 
    {this.views[this.views.length] = viewName;}
}

//--------------------------------------------------------------------------------------------------------------------------
/*
 * Determines if a view name is in the view list (and so in the DOM)
 * Returns index of the view or null if not in list
 * @viewName : the view name to test
 */
function GUI_isViewName(viewName)
{
	var index;
	for (viewNames in this.views)
	{
		if (this.views[viewNames] == viewName)
		{
			index = viewNames;
			break;
		}
	}
	return index;
}

//--------------------------------------------------------------------------------------------------------------------------
/*
 * Applies a callback on all existing views (in DOM)
 * @functionToApply : callback function
 */
function GUI_applyOnViewNames(functionToApply)
{
  applyOnArray(this,this.views,functionToApply);
}

//--------------------------------------------------------------------------------------------------------------------------
/*
 * Add a view name to current views list
 * @viewName : the view name to add
 */
function GUI_addCurrentViewName(viewName)
{
	var exists = false;
	for (currentViewNames in this.currentViews)
	{
		if (this.currentViews[currentViewNames] == viewName)
		{
			 exists = true;
			 break;
		}
	}
	if (!exists) 
	{
    this.currentViews[this.currentViews.length] = viewName;
    this.addViewName(viewName);
  }
}

//--------------------------------------------------------------------------------------------------------------------------
/*
 * Remove a view name from current views list
 * @viewName : the view name to remove
 */
function GUI_removeCurrentViewName(viewName)
{
	//find index of element to delete
	var index;
	for (currentViewNames in this.currentViews)
	{
		if (this.currentViews[currentViewNames] == viewName)
		{
			index = currentViewNames;
			break;
		}
	}
	if (index) 
    {this.currentViews.splice(index,1);}
}

//--------------------------------------------------------------------------------------------------------------------------
/*
 * Applies a callback on all current views (in DOM and visible)
 * @functionToApply : callback function
 */
function GUI_applyOnCurrentViewsNames(functionToApply)
{
  applyOnArray(this,this.currentViews,functionToApply);
}


//--------------------------------------------------------------------------------------------------------------------------
/*
 * Tests if a view is visible (i.e. in current views list
 * @viewName : the view name to test
 */
function GUI_isVisible(viewName)
{
  if (this.currentViews[viewName]) 
    {return true;}
  return false;
}
 
//--------------------------------------------------------------------------------------------------------------------------
/*
 * Show a specific view. If a showFunction is passed, it will be used. If not, the showFunction of  the list will be used. If it's not set either, object will be simply displayed
 * Notes : 
 *   - after displaying view, will try to load children
 *   - if view is the form, focus will be set on the first input text
 * @viewName : the name of the view to show
 * @showFunction : the function used to show (e.g. : for animation)
 */
function GUI_show(viewName,showFunction)
{
	var showFunctionUsed = showFunction || this.parentList.userShowFunction;
	var viewObj = this.getView(viewName);
	if (viewObj)	
	{
		if (!showFunctionUsed) 
		  {viewObj.style.display = "";}
		else
		  {showFunctionUsed(viewObj,viewName);}
		this.addCurrentViewName(viewName);
		if (viewName===defaultSettings.formViewName)
		{
			$("input[type='text']:visible,select:visible,textarea:visible", viewObj).eq(0).focus();
		}
		GG.api.loadChildren(viewObj);
	}
	else
	{
		GG.api.log("Error while trying to show "+this.client_id+" : viewObj not found for '"+viewName+"' view");
	}
}

//--------------------------------------------------------------------------------------------------------------------------
/*
 * Show all views. 
 * @viewName : the name of the view to show
 * @showFunction : the function used to show (e.g. : for animation)
 */
function GUI_showAll(showFunction)
{
	this.applyOnCurrentViewsNames(function(_this,viewName,showFunction) {
                              _this.show(viewName,showFunction);
                           });
}

//--------------------------------------------------------------------------------------------------------------------------
/*
 * Hide a specific view. If a hideFunction is passed, it will be used. If not, the hideFunction of  the list will be used. If it's not set either, object will be simply undisplayed. 
 * NB : it will be removed from the current views list
 * @viewName : the name of the view to hide
 * @hideFunction : the function used to hide (e.g. : for animation)
 */
function GUI_hide(viewName,hideFunction)
{
	var hideFunctionUsed = hideFunction || this.parentList.userHideFunction;
	var viewObj = this.getView(viewName);	
	if (viewObj)	
	{
		if (!hideFunctionUsed) 
			{viewObj.style.display = "none";}
		else
			{hideFunctionUsed(viewObj,viewName);}
		this.removeCurrentViewName(viewName);  
	}
	else
	{
		GG.api.log("Error while trying to show "+this.client_id+" : viewObj not found for '"+viewName+"' view");
	}
}

//--------------------------------------------------------------------------------------------------------------------------
/*
 * Hide all views. 
 * NB : the current views list will be emptied
 * @viewName : the name of the view to hide
 * @hideFunction : the function used to hide (e.g. : for animation)
 */
function GUI_hideAll(hideFunction)
{
	this.applyOnCurrentViewsNames(function(_this,viewName,hideFunction) {
                              _this.hide(viewName,hideFunction);
                           });
	this.currentViews = []; //Not really necessary... (done element by element in hide function)
}

//--------------------------------------------------------------------------------------------------------------------------
/*
 * Refresh all views available in DOM
 */
function GUI_refresh()
{ 
	this.applyOnViewNames(function(_this,viewName) {
                              dataMapping(_this,_this.getView(viewName),viewName);
                         });	
}
 
//--------------------------------------------------------------------------------------------------------------------------
/*
 * Returns the pattern id (i.e. the id of the DOM object to use as a pattern)
 * @viewName : the name of the view to get its pattern id
 */
function GUI_getPatternId(viewName)
{
  if (viewName && viewName!= "") 
    {return "pattern_" + this.object_type + "_" + viewName;} //case of GUI patterns
  else
  {
	GG.api.log("Trying to get a pattern id without a view name on unique_id=" + this.unique_id + " => '' returned","W");
	return "";
  }
}

//--------------------------------------------------------------------------------------------------------------------------
/*
 * Returns the pattern id by choosing between GuiList and Gui versions  : Gui version overrides GuiList version
 * @viewName : the name of the view to get its pattern id
 */
function GUI_determinePatternId(viewName)
{
  if (!this.getPatternId(viewName) && this.parentList)
  {
    return this.parentList.getPatternId(viewName);
  }
  else
  {
    return this.getPatternId(viewName);
  }
}

//--------------------------------------------------------------------------------------------------------------------------
/*
 * Creates a specific view by mapping a Gui object to a pattern
 * @viewName : the name of the view to be mapped
 */
function GUI_mapPattern(viewName)
{
	return mapPattern(this, viewName);
}

//--------------------------------------------------------------------------------------------------------------------------
/*
 * Returns a unique id per object and view. Used as a DOM object id for each view of an object
 * @viewName : the view name considered
 */
function GUI_getViewId(viewName)
{
	return "GUI_"+this.client_id+"_"+viewName;
}

//--------------------------------------------------------------------------------------------------------------------------
/*
 * Gets the DOM object corresponding to a view name in the viewsCache object for faster access, if available.Returns null if not.
 * @viewName : the view name to get
 */
function GUI_getViewFromCache(viewName)
{
  if (this.viewsCache[viewName])
    {return this.viewsCache[viewName];}
  else
  {
    if (this.isViewName(viewName))
    {
      //If view name exists, then it can be found in the DOM :
      this.viewsCache[viewName] = document.getElementById(this.getViewId(viewName));
    }
    else
    {
      //TODO : tests if this is always correct : can we have a deleted view that's still in the DOM ?
      return null;
    }
    return this.viewsCache[viewName];
  }
}

//--------------------------------------------------------------------------------------------------------------------------
/*
 * Returns the DOM object corresponding to a view. If the view does not exists in the DOM, it will be created
 * @viewName : the view name to get
 */
function GUI_getView(viewName)
{
	var viewObj = this.getViewFromCache(viewName);
	if (viewObj)
	{
		return viewObj;
	}
	else
	{
		viewObj = this.mapPattern(viewName);
		if (viewObj)
		{
		  this.viewsCache[viewName] = viewObj;
		  this.addViewName(viewName);
		  this.setDragAndDrop(viewName);
		}
		else
		{
		  GG.api.log("Couldn't find a mapping for "+this.serialize());
		  GG.api.log("(Was looking for "+this.determinePatternId(viewName)+")");
		}
		return viewObj;
	}
}

//--------------------------------------------------------------------------------------------------------------------------
/*
 * Display only one view of an object. NB : it will hide all other views of this object
 * @viewName : the view name to show
 * @hideFunction : the function used to hide (e.g. : for animation)
 * @showFunction : the function used to show (e.g. : for animation)
 */
function GUI_goToView(viewName,hideFunction,showFunction)
{
	//Prepare view to show to avoid flickering
	var viewObj = this.getView(viewName);
	
	//hide all other views
	this.applyOnCurrentViewsNames(function(_this,currentViewName) {
                                    if (currentViewName!= viewName) 
                                      {_this.hide(currentViewName,hideFunction);}
                                });
	this.show(viewName,showFunction);
}

//--------------------------------------------------------------------------------------------------------------------------
/**
 * Remove all views from DOM. 
 * NB : current views list will be emptied, and view cache will be lost
 */
function GUI_deleteViews()
{
	this.applyOnViewNames(function(_this,viewName) {
                              $("#"+_this.getViewId(viewName)).remove();
                           });
  this.viewsCache = {};
	this.currentViews = [];
}

//--------------------------------------------------------------------------------------------------------------------------
/**
 * Set all attributes, commons and custom
 * @attributesList : the attributes object
 */
function GUI_setAttributes(attributesList)
{
	if (attributesList)
	{
		this.unique_id = attributesList.unique_id;
		this.object_id = attributesList.object_id;
		this.parent = attributesList.parent;
		this.object_type = attributesList.object_type;
		this.deleted = attributesList.deleted;
		this.order_rank = attributesList.order_rank;
		this.deleted_at = attributesList.deleted_at;
		this.created_at = attributesList.created_at;
		this.attributes.setAttributes(attributesList.attributes);
  	}
}

//--------------------------------------------------------------------------------------------------------------------------
/**
 * Set all attributes, commons and custom
 * @attributesList : the attributes object
 */
function GUI_setAttribute(name,value,dataType)
{
	this.attributes.setAttribute(name,value,dataType);
}

//--------------------------------------------------------------------------------------------------------------------------
/**
 * Get an attribute by its name (only for custom attributes)
 * @name : the attribute name to get
 */
 function GUI_getAttribute(name)
{
	return this.attributes.getAttribute(name);
}

//--------------------------------------------------------------------------------------------------------------------------
/**
 * Get custom attributes list
 */
function GUI_getAttributes()
{
	return this.attributes.attributes;
}

//--------------------------------------------------------------------------------------------------------------------------
/**
 * Set a custom attribute by its name, from the form view.
 * @name : the attribute name to set
 */
function GUI_setAttributeByName(name)
{
	var obj = $("#"+this.getViewId(defaultSettings.formViewName)).find("[name="+name+"]")[0];
	var val = "";
	var dataType = "";
	if (obj)
	{
    var obj2 = $(obj);
    if (obj2.is("input"))
    {
      
      if (obj2.attr("type") == "checkbox")
        {val = (obj2.attr("checked") ? "1" : "0");} 
      else
        {val = obj2.val();}
    }
    else
      {val = obj2.html();}
    dataType = getNodeAttribute(obj,"dataType");
  }
	this.setAttribute(name,val,dataType);
}

//--------------------------------------------------------------------------------------------------------------------------
/**
 * Set a common attribute by its name, from the form view.
 * @name : the attribute name to set
 */
function GUI_setCommonAttributeByName(name)
{
	var obj = $("#"+this.getViewId(defaultSettings.formViewName)).find("[name="+name+"]")[0];
	var val = "";
	if (obj)
	{
    var obj2 = $(obj);
    if (obj2.is("input"))
    {
      
      if (obj2.attr("type") == "checkbox")
        {val = (obj2.attr("checked") ? "1" : "0");}  
      else
        {val = obj2.val();}
    }
    else
      {val = obj2.html();}
  }
	this[name] = val;
}
		
//--------------------------------------------------------------------------------------------------------------------------
/**
 * Check the value of a field : TODO !
 */
function GUI_validate()
{
	//TODO : seulement des validations custom pour l'instant : fonction à surclasser.
	/*var objProperty;
	var requiredMessage;
	alert('TODO avec objet attributes : ne marche plus');
	for (property in this.attributes)
	{
		objProperty = document.getElementById(this.getViewId(defaultSettings.formViewName)+"_"+property);
		requiredMessage = getNodeAttribute(objProperty,"requiredMessage");
		if (requiredMessage && requiredMessage!="" && objProperty && trim(objProperty.value)=="")
		{
			var message = new Message({type:"E",text:requiredMessage});
			this.displayMessage(message);
			return false;			
		}
	}*/
	return true;
}

//--------------------------------------------------------------------------------------------------------------------------
/**
 * Apply edit of an object in the form view : the JS object will be changed reflecting the form view
 * @formView : the name of the form view.
 */
function GUI_applyEdit(formView)
{
	if (this.validate())
	{
		//Standard attributes
		//These can't change manually : unique_id, object_id, object_type, deleted_at, created_at, order_rank
		this.setCommonAttributeByName("parent");
		this.setCommonAttributeByName("deleted");
		
		//Custom attributes: inputs in form view not being common attributes
		//TODO : don't use jQuery stuff except on pattern layer
		var attributesList = $('#' + this.getViewId(formView)).find("input").each(
        function() {
          if (this.name != "unique_id" && this.name != "object_id" && this.name != "object_type" && this.name != "deleted_at" && this.name != "created_at" && this.name != "order_rank")
            {GG.api.getGG(this).setAttributeByName(this.name);}
        });
    this.save();
	}
}

//--------------------------------------------------------------------------------------------------------------------------
/**
 * Set the object type
 * @objectType : the object type value to set
 */
function GUI_setObjectType(objectType)
{
	this.object_type = objectType;
} 

//--------------------------------------------------------------------------------------------------------------------------
/**
 * Set the datasource url (a quick access to the dataSource attribute of Gui's serverInterface attribute)
 * @dataSource : the dataSource url to set
 */
function GUI_setDataSource(dataSource)
{
	this.serverInterface.setDataSource(dataSource);
} 

//--------------------------------------------------------------------------------------------------------------------------
/**
 * Set the call method (a quick access to the callMethod attribute of Gui's serverInterface attribute)
 * @dataSource : the dataSource url to set
 */
function GUI_setCallMethod(callMethod)
{
	this.serverInterface.setCallMethod(callMethod);
}

//--------------------------------------------------------------------------------------------------------------------------
/**
 * Get the call method (a quick access to the callMethod attribute of Gui's serverInterface attribute)
 * @dataSource : the dataSource url to set
 */
function GUI_getCallMethod()
{
	return this.serverInterface.getCallMethod();
}

//--------------------------------------------------------------------------------------------------------------------------
/**
 * Save the Gui object sending it to the dataSource set, using the callMethod. Common attributes are automatically added if iframe call (i.e. POST)
 */
function GUI_save()
{
	//Generate view form if it doesn't exists
	this.getView(defaultSettings.formViewName);
	
	this.onRemoteCall = function (jsonData) {
                              this.onSaveCommon(jsonData);
                      };
	if (this.getCallMethod() == "iframe")
	{
		var formToSave = document.getElementById(this.client_id+"_form");
		this.serverInterface.addHiddenInput(formToSave,'unique_id',this.unique_id);
		this.serverInterface.addHiddenInput(formToSave,'object_id',this.object_id);
		this.serverInterface.addHiddenInput(formToSave,'object_type',this.object_type);		
		this.serverInterface.addHiddenInput(formToSave,'order_rank',this.order_rank);		
		this.serverInterface.addHiddenInput(formToSave,'created_at',this.created_at);		
		this.serverInterface.addHiddenInput(formToSave,'deleted',this.deleted);		
		this.serverInterface.addHiddenInput(formToSave,'deleted_at',this.deleted_at);		
		if (this.parentList && this.parentList.parentGUI)   
			{this.serverInterface.addHiddenInput(formToSave,'parent',this.parentList.parentGUI.object_id);}
		this.attributes.addToForm(formToSave,this);
		
		this.serverInterface.remoteCall("update",null,this.client_id,formToSave);		
	}
	else
	{
		this.serverInterface.remoteCall("update",this.serialize(),this.client_id);
	}
	
}

//--------------------------------------------------------------------------------------------------------------------------
/**
 * Callback function of save action : common part
 * @jsonData : the json data sent by server side
 */
function GUI_onSaveCommon(jsonData)
{
	if (jsonData) 
	{
		this.setAttributes(jsonData.result);
		this.refresh();
		this.onSave();
		if (jsonData.error)
			{defaultSettings.displayMessage(jsonData.error,"E");}
		if (jsonData.debug)
			{GG.api.log(jsonData.debug,"I");}	
	}
}

//--------------------------------------------------------------------------------------------------------------------------
/**
 * Specific part of onSave callbacks : to be overriden (empty by default)
 */
function GUI_onSave()
{}

//--------------------------------------------------------------------------------------------------------------------------
/**
 * Delete an object
 */
function GUI_remove()
{
	//Generate view form if don't exists
	this.getView(defaultSettings.formViewName);
	
	this.onRemoteCall = function (jsonData) {
                              this.onRemoveCommon(jsonData);
                       };
	if (this.serverInterface.callMethod != "iframe")
	{
		this.serverInterface.remoteCall("delete",this.serialize(),this.client_id);
	}
	else
	{
		var formToSave = document.getElementById(this.client_id+"_form");
		this.serverInterface.remoteCall("delete",null,this.client_id,formToSave);
	}
}

//--------------------------------------------------------------------------------------------------------------------------
/**
 * Callback function on the removal of an object : common part
 * @jsonData : the json data sent by server side
 */
 function GUI_onRemoveCommon(jsonData)
{
	if (jsonData) 
	{
		this.setAttributes(jsonData.result);
		if (this.deleted)
		{
      this.deleteViews();
      this.parentList.removeElement(this);
      this.onRemove();
    }
		if (jsonData.error)
			{defaultSettings.displayMessage(jsonData.error,"E");}
		if (jsonData.debug)
			{GG.api.log(jsonData.debug,"I");}				
	}
}

//--------------------------------------------------------------------------------------------------------------------------
/**
 * Specific part of onRemove callbacks : to be overriden (empty by default)
 */
function GUI_onRemove()
{}

//--------------------------------------------------------------------------------------------------------------------------
/*
 * Drag & Drop function
 * Based on jQuery UI D&D functions
 * @viewName : the viewName to apply D&D on
 */
function GUI_setDragAndDrop(viewName)
{
  var viewObj = this.viewsCache[viewName];
  
  //Draggable DOM Object
  if (!viewObj) 
    {GG.api.log("Error : setDragAndDrop on null DOM object (view '" + viewName + "' of GUI client_id " + this.client_id);}  
  var dragPoint = $(".dragpoint",viewObj)[0];
  if (dragPoint)
  {
    //dragPoint.id = viewObj.id + "_" + viewname + "_dragpoint";
    dragPoint.id = viewObj.id + "_dragpoint";
    $(viewObj).addClass("drag_"+this.object_type).addClass("drag_"+this.object_type+"_list").draggable(
      {
        handle:		$("#" + viewObj.id + "_dragpoint"),
        zIndex: 	defaultSettings.dragHelperZIndex || 1000,
        opacity: 	defaultSettings.dragHelperOpacity || 0.7,
		helper:		defaultSettings.dragHelperMode || "original",
        revert:   	true
      }
    );
  }
  
  //List of accepted dataType for Drop => made from the object_type attribute of the parent list
  var cssAcceptList = ".drag_"+this.object_type;
  if (this.parentList)
  {
	cssAcceptListParent = this.parentList.getDragAcceptClasses();
	if (cssAcceptListParent)
		{cssAcceptList = cssAcceptListParent;}
  }
	
  //Droppable DOM Object (for reorder D&D)
  GG.api.log(this.client_id + " accepts drops of class "+cssAcceptList );
  $(viewObj).droppable(
                {
                  accept : cssAcceptList, 
                  hoverClass : "dragAndDropHover",
                  greedy : true,
                  tolerance: defaultSettings.dropTolerance || 'intersect',
                  drop : function (event,ui) {
                            var dataObjectDragged = GG.api.getGG(ui.draggable.get(0));
                            
                            var dataObjectDrop = GG.api.getGG(this);
                            dataObjectDrop.parentList.moveElement(dataObjectDragged,dataObjectDrop);
                            dataObjectDragged.save();
                    }
                });
	
	//Droppable container
	//TODO : à ajouter à un conteneur vide, et enlever si non vide
	/*if (this.parentList)
	{
		var container;
		this.parentList.applyOnContainers(function (_this,container) {
                                          $(container).addClass("drop_"+_this.object_type).Droppable(
                                            {
                                              accept : "drag_"+_this.object_type, 
                                              onHover: function() {
                                                        $(this).css("border","1px solid #AA0000");
                                                       },
                                              onOut: function() {
                                                        $(this).css("border","");
                                                     },
                                              onDrop : function (draggedObj) {
                                                  var dataObjectDragged = GG.api.getGG(draggedObj);
                                                  var dataListDrop = GG.api.getGG(this);
                                                  if (!dataObjectDragged.parentList || (dataListDrop.id != dataObjectDragged.parentList.client_id))
                                                  {
                                                    dataListDrop.moveElement(dataObjectDragged,"last");
                                                    dataObjectDragged.save();
                                                  }
                                                  $(this).css("border","");
                                                }
                                            }
                                          );
                                        });
	}*/
}

//--------------------------------------------------------------------------------------------------------------------------
/**
 * Determine the rank string of the element within list
 */
function GUI_setRank()
{
  var previousElement = this.parentList.getPrevious(this);
  var nextElement = this.parentList.getNext(this);
  var rankToSet = "";
  if (!nextElement && !previousElement)
  {
    this.order_rank = 0;
    return;
  }
  if (!previousElement)
  {
    this.order_rank = nextElement.order_rank - 1;
    return;
  }
  if (!nextElement)
  {
    this.order_rank = previousElement.order_rank + 1;
    return;    
  }
  this.order_rank = (previousElement.order_rank + nextElement.order_rank) / 2;
  return;    
} 

//--------------------------------------------------------------------------------------------------------------------------
/*
 * Serialization of a Gui object (URL format)
 */
function GUI_serialize()
{
  var parentCritere = "";
  if (this.parentList && this.parentList.parentGUI) 
    {parentCritere += "&parent=" + serializeValue(this.parentList.parentGUI.object_id);}
  
  //TODO : use this correctly : set it like "if" above
  //+ "&parent="      + this.parent 
  return "client_id=" + serializeValue(this.client_id) + "&object_id=" + serializeValue(this.object_id) + "&object_type=" + serializeValue(this.object_type) + "&deleted=" + serializeValue(this.deleted) + "&order_rank=" + serializeValue(this.order_rank)+ "&deleted_at=" + serializeValue(this.deleted_at) + "&created_at="  + serializeValue(this.created_at) + parentCritere + this.attributes.serialize();
}

//--------------------------------------------------------------------------------------------------------------------------
/*
 * Serialization of a Gui object (JSON format)
 */
function GUI_toJSON()
{
  var commons = this.commonAttributeToJSON("client_id") + "," + this.commonAttributeToJSON("object_id") + "," + this.commonAttributeToJSON("parent") + "," + this.commonAttributeToJSON("object_type") + "," + this.commonAttributeToJSON("deleted") + "," + this.commonAttributeToJSON("order_rank") + "," + this.commonAttributeToJSON("deleted_at") + "," + this.commonAttributeToJSON("created_at");
  
  return "{ \"client_id\" : " + serializeValue(this.client_id) + ", " + commons +  ", \"attributes\": " + this.attributes.toJSON() + "}";
}

//--------------------------------------------------------------------------------------------------------------------------
/*
 * Serialization of a Gui object (JSON-RPC format)
 */
function GUI_toJSON_RPC(method)
{
  var commons = this.commonAttributeToJSON("client_id") + "," + this.commonAttributeToJSON("object_id") + "," + this.commonAttributeToJSON("parent") + "," + this.commonAttributeToJSON("object_type") + "," + this.commonAttributeToJSON("deleted") + "," + this.commonAttributeToJSON("order_rank") + "," + this.commonAttributeToJSON("deleted_at") + "," + this.commonAttributeToJSON("created_at");
  
  return "{ \"method\" : \"" + serializeValue(method) + "\", \"client_id\" : " + serializeValue(this.client_id) + ", \"params\":[{  " + commons +  ", \"attributes\": " + this.attributes.toJSON() + "}]}";
}

//--------------------------------------------------------------------------------------------------------------------------
/*
 * Serialization of a common attribute (JSON format)
 */
function GUI_commonAttributeToJSON(attributeName)
{
  return "\"" + attributeName + "\": \"" + serializeValue(this[attributeName]) + "\"";
}

//--------------------------------------------------------------------------------------------------------------------------
/*
 * Attach the current Gui object to a parent list (i.e. a GuiList object)
 * @parentList : the parent GuiList object
 */
function GUI_setParentList(parentList)
{
  this.parentList = parentList;
  this.setCallMethod(parentList.getCallMethod());
  this.setDataSource(parentList.getDataSource());
}

//--------------------------------------------------------------------------------------------------------------------------
/**
 * GUI objects reference
 */
var GUIs = []; //Data objects list
function getGUI(client_id)
{
	for (var i=0; i<GUIs.length; i++)
	{
		if (GUIs[i].client_id == client_id)
		{
			return GUIs[i];
		}
	}
}

//--------------------------------------------------------------------------------------------------------------------------
/*
 * Applies a function to all Gui objects
 * @functionToApply : function to apply
 */
function applyOnGUIs(functionToApply) {
    applyOnArray(null,GUIs,functionToApply);
}

//--------------------------------------------------------------------------------------------------------------------------
// Ids
//--------------------------------------------------------------------------------------------------------------------------

var GUIIds = 0;
/**
 * Get a new GUI id
 */
function getNewGUIId()
{
	GUIIds ++;
	return GUIIds;
}

/**
 * Add a GUI to GUIs reference
 */
function addToGUIs(newGUI)
{
	GUIs[GUIs.length]=newGUI;
}

